package cn.xinfei.xdecision.engine.runner.service;

import cn.xinfei.xdecision.common.dynamic.DataSourceType;
import cn.xinfei.xdecision.common.dynamic.DynamicDataSourceSwitch;
import cn.xinfei.xdecision.common.dynamic.DynamicDatasourceContextHolder;
import cn.xinfei.xdecision.common.utils.util.DateUtils;
import cn.xinfei.xdecision.common.utils.util.JsonUtils;
import cn.xinfei.xdecision.engine.runner.context.PipelineContext;
import com.alibaba.fastjson.JSONObject;
import com.mongodb.BasicDBObject;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.result.InsertOneResult;
import com.mongodb.client.result.UpdateResult;
import lombok.extern.slf4j.Slf4j;
import org.bson.BsonObjectId;
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.util.Calendar;
import java.util.Date;
import java.util.List;

@Slf4j
@Service
public class MongoTemplateProxy {

    @Autowired
    @Qualifier("examMongoTemplate")
    private MongoTemplate examMongoTemplate;

    @Autowired
    @Qualifier("mongoTemplate")
    private MongoTemplate mongoTemplate;

    @DynamicDataSourceSwitch
    public String updateSnapshotContextState(String coll, PipelineContext pipelineContext) {
        MongoTemplate mongoTemplate = getMongoTemplate();
        Document doc = Document.parse(JsonUtils.toJsonString(pipelineContext));

        String decisionId = pipelineContext.getDecisionId();
        String bizId = pipelineContext.getRequestId();
        String engineCode = pipelineContext.getEngineCode();

        String objId = null;

        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.DAY_OF_MONTH, -7);


        Update update = new Update();
        update.set("code", pipelineContext.getCode());
        update.set("state", pipelineContext.getState());
        update.set("result", pipelineContext.getResult());
        update.set("nodeCodeStack", JSONObject.toJSON(pipelineContext.getNodeCodeStack()));
        update.set("updateTime", new Date().getTime());


        Criteria criteriaDefinition = Criteria
                .where("decisionId").is(decisionId)
                .and("requestId").is(bizId)
                .and("engineCode").is(engineCode)
                .and("createTime").gt(DateUtils.getBeforeNumDayDate(1));
//                .and("createTime").gt(instance.getTime().getTime());
        Query query = new Query(criteriaDefinition);
//        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, PipelineContext.class, coll);
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, coll);

        boolean b = false;
        if (null != updateResult.getUpsertedId()) {
            BsonObjectId bsonObjectId = updateResult.getUpsertedId().asObjectId();
            objId = bsonObjectId.getValue().toString();
            b = true;
            log.info("upsert state mongodb success,coll={},doc={},result={},decisionId={},requestId={}", coll, objId, b, decisionId, bizId);
        } else if ((updateResult.getMatchedCount() > 0 && updateResult.getModifiedCount() > 0)) {
            b = true;
            log.info("update state mongodb success,coll={},doc={},result={},decisionId={},requestId={},updateResult={}", coll, objId, b, decisionId, bizId, updateResult);
            return updateResult.getModifiedCount() + "";
//            return "true";
        } else {
            log.info("update state mongodb fail,coll={},doc={},result={},decisionId={},requestId={},updateResult={}", coll, objId, b, decisionId, bizId, updateResult);
            return null;
        }

        return objId;
    }

    @DynamicDataSourceSwitch
    public String upsertOneDecisionDetail(String coll, Update update, Query query, String decisionId, String bizId) {
//        UpdateResult updateResult = mongoTemplate.updateMulti(query, update,  coll);
        UpdateResult updateResult = null;
        try {
//            log.info("query={},update={}",query,update);
            updateResult = mongoTemplate.upsert(query, update, coll);
        } catch (DuplicateKeyException e) {
            // 暂时 todo shiyusen
            log.warn("upsert fail. retry update. msg={}", e.getMessage());
//            try {
//                updateResult = mongoTemplate.updateMulti(query, update, coll);
//            } catch (Exception e1) {
//                log.error("updateMulti fail. msg={}", e.getMessage(), e1);
//            }
        } catch (Exception e) {
            log.error("upsert fail. msg={},query={},update={}", e.getMessage(), query,update,e);
        }

        boolean b = false;
        String objId = null;
        if (updateResult != null && null != updateResult.getUpsertedId()) {
            BsonObjectId bsonObjectId = updateResult.getUpsertedId().asObjectId();
            objId = bsonObjectId.getValue().toString();
            b = true;
            log.info("upsert mongodb success,coll={},doc={},result={},decisionId={},requestId={}", coll, objId, b, decisionId, bizId);
            return objId;
        } else if (updateResult != null && (updateResult.getMatchedCount() > 0 && updateResult.getModifiedCount() > 0)) {
            b = true;
            log.info("update mongodb success,coll={},result={},decisionId={},requestId={}，updateResult={}", coll, b, decisionId, bizId, updateResult);
            return updateResult.getModifiedCount() + "";
        } else {
            log.info("update mongodb fail,coll={},doc={},result={},decisionId={},requestId={},updateResult={}", coll, objId, b, decisionId, bizId, updateResult);
            return null;
        }
    }

    @DynamicDataSourceSwitch
    public String updateOne(String coll, PipelineContext pipelineContext, Document document) {
        MongoTemplate mongoTemplate = getMongoTemplate();
        pipelineContext.setUpdateTime(DateUtils.getCurrentStrDate());

        String decisionId = pipelineContext.getDecisionId();
        String bizId = pipelineContext.getRequestId();
        String engineCode = pipelineContext.getEngineCode();

        String objId = null;

        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.DAY_OF_MONTH, -7);


        Document doc = Document.parse(JsonUtils.toJsonString(pipelineContext));
        Update update = Update.fromDocument(doc, "");
        Criteria criteriaDefinition = Criteria
                .where("_id").is(document.getObjectId("_id"));
        Query query = new Query(criteriaDefinition);
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, coll);

        boolean b = false;
        if (null != updateResult.getUpsertedId()) {
            BsonObjectId bsonObjectId = updateResult.getUpsertedId().asObjectId();
            objId = bsonObjectId.getValue().toString();
            b = true;
            log.info("upsert mongodb success,coll={},doc={},result={},decisionId={},requestId={}", coll, objId, b, decisionId, bizId);
            return objId;
        } else if ((updateResult.getMatchedCount() > 0 && updateResult.getModifiedCount() > 0)) {
            b = true;
            log.info("update mongodb success,coll={},result={},decisionId={},requestId={}，updateResult={}", coll, b, decisionId, bizId, updateResult);
            return updateResult.getModifiedCount() + "";
        } else {
            log.info("update mongodb fail,coll={},doc={},result={},decisionId={},requestId={},updateResult={}", coll, objId, b, decisionId, bizId, updateResult);
            return null;
        }
    }

    @DynamicDataSourceSwitch
    public String updateOne(String coll, PipelineContext pipelineContext) {
        MongoTemplate mongoTemplate = getMongoTemplate();
        pipelineContext.setUpdateTime(DateUtils.getCurrentStrDate());
        Document doc = Document.parse(JsonUtils.toJsonString(pipelineContext));

        String decisionId = pipelineContext.getDecisionId();
        String bizId = pipelineContext.getRequestId();
        String engineCode = pipelineContext.getEngineCode();

        String objId = null;

        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.DAY_OF_MONTH, -7);


        Update update = Update.fromDocument(doc, "");
        Criteria criteriaDefinition = Criteria
                .where("decisionId").is(decisionId)
                .and("requestId").is(bizId)
                .and("engineCode").is(engineCode)
                .and("createTime").gt(DateUtils.getBeforeNumDayDate(1));
//                .and("createTime").gt(instance.getTime().getTime());
        Query query = new Query(criteriaDefinition);
//        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, PipelineContext.class, coll);
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, coll);

        boolean b = false;
        if (null != updateResult.getUpsertedId()) {
            BsonObjectId bsonObjectId = updateResult.getUpsertedId().asObjectId();
            objId = bsonObjectId.getValue().toString();
            b = true;
            log.info("upsert mongodb success,coll={},doc={},result={},decisionId={},requestId={}", coll, objId, b, decisionId, bizId);
            return objId;
        } else if ((updateResult.getMatchedCount() > 0 && updateResult.getModifiedCount() > 0)) {
            b = true;
            log.info("update mongodb success,coll={},result={},decisionId={},requestId={}，updateResult={}", coll, b, decisionId, bizId, updateResult);
            return updateResult.getModifiedCount() + "";
        } else {
            log.info("update mongodb fail,coll={},doc={},result={},decisionId={},requestId={},updateResult={}", coll, objId, b, decisionId, bizId, updateResult);
            return null;
        }

    }


    @DynamicDataSourceSwitch
    public String upsertOne(String coll, PipelineContext pipelineContext) {
        MongoTemplate mongoTemplate = getMongoTemplate();

        pipelineContext.setUpdateTime(DateUtils.getCurrentStrDate());
        Document doc = Document.parse(JsonUtils.toJsonString(pipelineContext));

        String decisionId = pipelineContext.getDecisionId();
        String bizId = pipelineContext.getRequestId();
        String engineCode = pipelineContext.getEngineCode();

        String objId = null;

        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.DAY_OF_MONTH, -7);


        Update update = Update.fromDocument(doc, "");
        Criteria criteriaDefinition = Criteria
                .where("decisionId").is(decisionId)
                .and("requestId").is(bizId)
                .and("engineCode").is(engineCode)
                .and("createTime").gt(DateUtils.getBeforeNumDayDate(1));
//                .and("createTime").gt(instance.getTime().getTime());
        Query query = new Query(criteriaDefinition);
//        UpdateResult updateResult = mongoTemplate.upsert(query, update, PipelineContext.class, coll);
        UpdateResult updateResult = mongoTemplate.upsert(query, update, coll);

        boolean b = false;
        if (null != updateResult.getUpsertedId()) {
            BsonObjectId bsonObjectId = updateResult.getUpsertedId().asObjectId();
            objId = bsonObjectId.getValue().toString();
            b = true;
        } else if ((updateResult.getMatchedCount() > 0 && updateResult.getModifiedCount() > 0)) {
            b = true;
            return updateResult.getModifiedCount() + "";
        }

        log.info("insert mongodb,coll={},doc={},result={},decisionId={},requestId={},updateResult={}", coll, objId, b, decisionId, bizId, updateResult);
        return objId;
    }

    @DynamicDataSourceSwitch
    public String upsertOne(String coll, String message) {
        MongoTemplate mongoTemplate = getMongoTemplate();
//        Document doc = Document.parse(JSONObject.parseObject(message).toJSONString());
        Document doc = Document.parse(message);

        String decisionId = doc.getString("decisionId");
        String bizId = doc.getString("requestId");
        String engineCode = doc.getString("engineCode");

        String objId = null;


        Update update = Update.fromDocument(doc, "");

        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.DAY_OF_MONTH, -7);
        Criteria criteriaDefinition = Criteria
                .where("decisionId").is(decisionId)
                .and("requestId").is(bizId)
                .and("engineCode").is(engineCode)
                .and("createTime").gt(DateUtils.getBeforeNumDayDate(1));
//                .and("createTime").gt(instance.getTime().getTime());
        Query query = new Query(criteriaDefinition);
        UpdateResult updateResult = mongoTemplate.upsert(query, update, coll);

        boolean b = false;
        if (null != updateResult.getUpsertedId()) {
            BsonObjectId bsonObjectId = updateResult.getUpsertedId().asObjectId();
            objId = bsonObjectId.getValue().toString();
            b = true;
        } else if ((updateResult.getMatchedCount() > 0 && updateResult.getModifiedCount() > 0)) {
            b = true;
            return updateResult.getModifiedCount() + "";
        }

        log.info("insert mongodb,coll={},doc={},result={},decisionId={},requestId={},updateResult={}", coll, objId, b, decisionId, bizId, updateResult);
        return objId;


    }

    @DynamicDataSourceSwitch
    public String insertOne(String coll, String message) {
        long start=System.currentTimeMillis();
        MongoTemplate mongoTemplate = getMongoTemplate();
        MongoCollection<Document> collection = mongoTemplate.getDb().getCollection(coll);
//        Document doc = Document.parse(JSONObject.parseObject(message).toJSONString());
        Document doc = Document.parse(message);


        InsertOneResult insertOneResult = collection.insertOne(doc);
        BsonValue insertedId = insertOneResult.getInsertedId();
        ObjectId value = insertedId.asObjectId().getValue();
        long end=System.currentTimeMillis();
        log.info("insert mongodb ,coll={},doc={},mongodbDuration={}", coll, value.toString(),end-start);
        return value.toString();

    }


    @DynamicDataSourceSwitch
    public Document findByDecisionId(String coll, String engineCode, String decisionId) {
        MongoTemplate mongoTemplate = getMongoTemplate();
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.DAY_OF_MONTH, -7);
        Criteria criteriaDefinition = Criteria
                .where("decisionId").is(decisionId)
                .and("engineCode").is(engineCode)
                .and("createTime").gt(DateUtils.getBeforeNumDayDate(1));
//                .and("createTime").gt(instance.getTime().getTime());
//                .and("createTime").gt(instance.getTime().getTime());

        Query query = new Query(criteriaDefinition);
        Document jsonObjects = mongoTemplate.findOne(query, Document.class, coll);
        return jsonObjects;
    }


    @DynamicDataSourceSwitch
    public List<Document> findByBizId(String coll, String bizId) {
        MongoTemplate mongoTemplate = getMongoTemplate();
        BasicDBObject dbObject = new BasicDBObject();
        dbObject.put("requestId", bizId);
        Query query = new BasicQuery(dbObject.toJson());
        List<Document> jsonObjects = mongoTemplate.find(query, Document.class, coll);
        return jsonObjects;
    }

    @DynamicDataSourceSwitch
    public Document findByBizId(String coll, String engineCode, String bizId, String decisionId) {
        MongoTemplate mongoTemplate = getMongoTemplate();
        BasicDBObject dbObject = new BasicDBObject();
        dbObject.put("requestId", bizId);
        dbObject.put("decisionId", decisionId);
        dbObject.put("engineCode", engineCode);
        Query query = new BasicQuery(dbObject.toJson());
        Document jsonObjects = mongoTemplate.findOne(query, Document.class, coll);
        return jsonObjects;
    }


    @DynamicDataSourceSwitch
    public PipelineContext findByDecisionId(String coll, String engineCode, String bizId, String decisionId) {
        MongoTemplate mongoTemplate = getMongoTemplate();
        BasicDBObject dbObject = new BasicDBObject();
        dbObject.put("requestId", bizId);
        dbObject.put("decisionId", decisionId);
        dbObject.put("engineCode", engineCode);
        Query query = new BasicQuery(dbObject.toJson());
        PipelineContext jsonObjects = mongoTemplate.findOne(query, PipelineContext.class, coll);
        return jsonObjects;
    }

    @DynamicDataSourceSwitch
    public List<Document> findByBizId(String coll, String engineCode, String bizId) {
        MongoTemplate mongoTemplate = getMongoTemplate();
        BasicDBObject dbObject = new BasicDBObject();
        dbObject.put("requestId", bizId);
        dbObject.put("engineCode", engineCode);
        Query query = new BasicQuery(dbObject.toJson());
        List<Document> jsonObjects = mongoTemplate.find(query, Document.class, coll);
        return jsonObjects;
    }


    public MongoTemplate getMongoTemplate() {
        DataSourceType dataSourceType = DynamicDatasourceContextHolder.getDataSourceType();
        if (dataSourceType == DataSourceType.exam) {
            return examMongoTemplate;
        }
        return mongoTemplate;
    }
}
